import os
from motor.motor_asyncio import AsyncIOMotorClient
from pymongo.errors import ConnectionFailure
from typing import List, Optional
from bson import ObjectId
from bson.errors import InvalidId
import uuid
from datetime import datetime, timezone
from models import Task, TaskCreate, TaskUpdate, FileMetadata
import logging

# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class Database:
    client: AsyncIOMotorClient = None
    database = None

database = Database()

async def connect_to_mongo():
    """连接到MongoDB"""
    try:
        mongodb_url = os.getenv("MONGODB_URL", "mongodb://localhost:27017")
        database_name = os.getenv("DATABASE_NAME", "task_manager")
        
        logger.info(f"正在连接到MongoDB: {mongodb_url}")
        database.client = AsyncIOMotorClient(mongodb_url)
        database.database = database.client[database_name]
        
        # 测试连接
        await database.client.admin.command('ping')
        logger.info("成功连接到MongoDB")
        
        # 创建索引
        await create_indexes()
        
    except ConnectionFailure as e:
        logger.error(f"MongoDB连接失败: {e}")
        raise
    except Exception as e:
        logger.error(f"数据库初始化错误: {e}")
        raise

async def close_mongo_connection():
    """关闭MongoDB连接"""
    if database.client:
        database.client.close()
        logger.info("MongoDB连接已关闭")

async def create_indexes():
    """创建数据库索引"""
    try:
        tasks_collection = database.database[os.getenv("TASKS_COLLECTION", "tasks")]
        files_collection = database.database[os.getenv("FILES_COLLECTION", "files")]
        
        # 任务索引
        await tasks_collection.create_index("created_at")
        await tasks_collection.create_index("status")
        await tasks_collection.create_index("category")
        await tasks_collection.create_index("deadline")
        
        # 文件索引
        await files_collection.create_index("uploaded_at")
        await files_collection.create_index("task_id")
        await files_collection.create_index("original_name")
        
        logger.info("数据库索引创建完成")
    except Exception as e:
        logger.error(f"创建索引失败: {e}")

class TaskService:
    @staticmethod
    async def create_task(task_data: TaskCreate) -> Task:
        """创建新任务"""
        try:
            collection = database.database[os.getenv("TASKS_COLLECTION", "tasks")]
            
            # 生成唯一ID
            task_id = str(uuid.uuid4())
            
            # 准备任务数据
            task_dict = task_data.dict(by_alias=True)
            task_dict["_id"] = task_id
            task_dict["createdAt"] = datetime.now(timezone.utc)
            task_dict["updatedAt"] = datetime.now(timezone.utc)
            
            # 插入任务
            result = await collection.insert_one(task_dict)
            
            if result.inserted_id:
                # 返回创建的任务
                created_task = await collection.find_one({"_id": task_id})
                return Task(**created_task)
            else:
                raise Exception("任务创建失败")
                
        except Exception as e:
            logger.error(f"创建任务失败: {e}")
            raise

    @staticmethod
    async def get_task(task_id: str) -> Optional[Task]:
        """根据ID获取任务"""
        try:
            collection = database.database[os.getenv("TASKS_COLLECTION", "tasks")]
            task_data = await collection.find_one({"_id": task_id})
            
            if task_data:
                return Task(**task_data)
            return None
            
        except Exception as e:
            logger.error(f"获取任务失败: {e}")
            return None

    @staticmethod
    async def get_all_tasks() -> List[Task]:
        """获取所有任务"""
        try:
            collection = database.database[os.getenv("TASKS_COLLECTION", "tasks")]
            cursor = collection.find({}).sort("createdAt", -1)
            
            tasks = []
            async for task_data in cursor:
                tasks.append(Task(**task_data))
            
            return tasks
            
        except Exception as e:
            logger.error(f"获取任务列表失败: {e}")
            return []

    @staticmethod
    async def update_task(task_id: str, task_update: TaskUpdate) -> Optional[Task]:
        """更新任务"""
        try:
            collection = database.database[os.getenv("TASKS_COLLECTION", "tasks")]
            
            # 准备更新数据
            update_data = {k: v for k, v in task_update.dict(by_alias=True, exclude_unset=True).items() if v is not None}
            update_data["updatedAt"] = datetime.now(timezone.utc)
            
            # 更新任务
            result = await collection.update_one(
                {"_id": task_id},
                {"$set": update_data}
            )
            
            if result.modified_count > 0:
                # 返回更新后的任务
                updated_task = await collection.find_one({"_id": task_id})
                return Task(**updated_task)
            
            return None
            
        except Exception as e:
            logger.error(f"更新任务失败: {e}")
            return None

    @staticmethod
    async def delete_task(task_id: str) -> bool:
        """删除任务"""
        try:
            collection = database.database[os.getenv("TASKS_COLLECTION", "tasks")]
            result = await collection.delete_one({"_id": task_id})
            
            return result.deleted_count > 0
            
        except Exception as e:
            logger.error(f"删除任务失败: {e}")
            return False

    @staticmethod
    async def get_tasks_by_status(status: str) -> List[Task]:
        """根据状态获取任务"""
        try:
            collection = database.database[os.getenv("TASKS_COLLECTION", "tasks")]
            cursor = collection.find({"status": status}).sort("createdAt", -1)
            
            tasks = []
            async for task_data in cursor:
                tasks.append(Task(**task_data))
            
            return tasks
            
        except Exception as e:
            logger.error(f"根据状态获取任务失败: {e}")
            return []

    @staticmethod
    async def get_tasks_by_category(category: str) -> List[Task]:
        """根据分类获取任务"""
        try:
            collection = database.database[os.getenv("TASKS_COLLECTION", "tasks")]
            cursor = collection.find({"category": category}).sort("createdAt", -1)
            
            tasks = []
            async for task_data in cursor:
                tasks.append(Task(**task_data))
            
            return tasks
            
        except Exception as e:
            logger.error(f"根据分类获取任务失败: {e}")
            return []

class FileService:
    @staticmethod
    async def save_file_metadata(file_metadata: FileMetadata) -> FileMetadata:
        """保存文件元数据"""
        try:
            collection = database.database[os.getenv("FILES_COLLECTION", "files")]
            
            # 生成唯一ID
            file_id = str(uuid.uuid4())
            
            # 准备文件数据
            file_dict = file_metadata.model_dump(by_alias=True, exclude_unset=True)
            file_dict["_id"] = file_id
            file_dict["uploaded_at"] = datetime.now(timezone.utc)
            
            # 插入文件记录
            result = await collection.insert_one(file_dict)
            
            if result.inserted_id:
                # 返回创建的文件记录
                created_file = await collection.find_one({"_id": file_id})
                if created_file:
                    # 确保_id字段正确映射为id
                    created_file["id"] = created_file["_id"]
                    return FileMetadata(**created_file)
                else:
                    raise Exception("无法获取创建的文件记录")
            else:
                raise Exception("文件元数据保存失败")
                
        except Exception as e:
            logger.error(f"保存文件元数据失败: {e}")
            raise

    @staticmethod
    async def get_file_metadata(file_id: str) -> Optional[FileMetadata]:
        """获取文件元数据"""
        try:
            collection = database.database[os.getenv("FILES_COLLECTION", "files")]
            file_data = await collection.find_one({"_id": file_id})
            
            if file_data:
                # 确保_id字段正确映射为id
                if "_id" in file_data and "id" not in file_data:
                    file_data["id"] = file_data["_id"]
                return FileMetadata(**file_data)
            return None
            
        except Exception as e:
            logger.error(f"获取文件元数据失败: {e}")
            return None

    @staticmethod
    async def delete_file_metadata(file_id: str) -> bool:
        """删除文件元数据"""
        try:
            collection = database.database[os.getenv("FILES_COLLECTION", "files")]
            result = await collection.delete_one({"_id": file_id})
            
            return result.deleted_count > 0
            
        except Exception as e:
            logger.error(f"删除文件元数据失败: {e}")
            return False

    @staticmethod
    async def get_all_files() -> List[FileMetadata]:
        """获取所有文件"""
        try:
            collection = database.database[os.getenv("FILES_COLLECTION", "files")]
            cursor = collection.find({}).sort("uploaded_at", -1)
            
            files = []
            async for file_data in cursor:
                # 确保_id字段正确映射为id
                if "_id" in file_data and "id" not in file_data:
                    file_data["id"] = file_data["_id"]
                files.append(FileMetadata(**file_data))
            
            return files
            
        except Exception as e:
            logger.error(f"获取文件列表失败: {e}")
            return []

    @staticmethod
    async def get_files_by_task(task_id: str) -> List[FileMetadata]:
        """根据任务ID获取文件"""
        try:
            collection = database.database[os.getenv("FILES_COLLECTION", "files")]
            cursor = collection.find({"task_id": task_id}).sort("uploaded_at", -1)
            
            files = []
            async for file_data in cursor:
                # 确保_id字段正确映射为id
                if "_id" in file_data and "id" not in file_data:
                    file_data["id"] = file_data["_id"]
                files.append(FileMetadata(**file_data))
            
            return files
            
        except Exception as e:
            logger.error(f"根据任务获取文件失败: {e}")
            return []
